package znet

import (
	"errors"
	"fmt"
	"io"
	"net"
	"sync"
	"zinx/utils"
	"zinx/ziface"
)

/*
*
链接模块
*/
type Connection struct {

	// 当前Conn属于哪个server
	TcpServer ziface.IService

	// 当前链接的socket TCP套接字
	Conn *net.TCPConn

	// 链接的ID
	ConnID uint32

	// 当前链接状态
	isClosed bool

	// 告知当前链接已经退出/停止 channel(由Reader告知Writer退出)
	ExitChan chan bool

	// 无缓存的管道，用于读写之间通信
	msgChan chan []byte

	// msgId对应处理的方法
	MsgHandle ziface.IMsgHandle

	// 链接属性集合
	property map[string]interface{}

	// 保护链接属性的锁
	propertyLock sync.RWMutex
}

// 初始换链接模块的方法
func NewConnection(server ziface.IService, conn *net.TCPConn, connID uint32, handle ziface.IMsgHandle) *Connection {
	c := &Connection{
		TcpServer: server,
		Conn:      conn,
		ConnID:    connID,
		isClosed:  false,
		msgChan:   make(chan []byte),
		MsgHandle: handle,
		ExitChan:  make(chan bool, 1),
	}
	// 将conn加入到ConnManager中
	c.TcpServer.GetConnManager().Add(c)

	return c
}

// 链接读业务方法
func (c *Connection) StartReader() {
	fmt.Println("Reader Goroutine is running")
	defer fmt.Println("connID=", c.ConnID, "Reader is exit ,remote addr is", c.RemoteAddr().String())
	defer c.Stop() // defer延迟执行，在退出函数前执行的操作

	for {
		// 读取客户端的数据到buf中，最大512字节
		//buf := make([]byte, utils.GlobalObject.MaxPackageSize)
		//_, err := c.Conn.Read(buf)
		//if err != nil {
		//	fmt.Println("recv buf err", err)
		//	continue
		//}

		// 创建一个拆包解包对象
		dp := NewDataPack()

		// 读取客户端的Msg Head 二进制流 8个字节
		headData := make([]byte, dp.GetHeadLen())
		_, err := io.ReadFull(c.GetTCPConnection(), headData)
		if err != nil {
			fmt.Println("read msg head error", err)
			break
		}

		// 拆包，得到msgID和msgDataLen 放到msg消息中
		msg, err := dp.UnPack(headData)
		if err != nil {
			fmt.Println(" unpack error", err)
			break
		}

		// 根据当前dataLen 再次读取Data,放在msg.Data中
		var data []byte
		if msg.GetDataLen() > 0 {
			data = make([]byte, msg.GetDataLen())
			_, err := io.ReadFull(c.GetTCPConnection(), data)
			if err != nil {
				fmt.Println("read msg data error", err)
			}
		}
		msg.SetData(data)
		// 得到当前conn数的Request请求数据

		// 从路由中获取对应的处理方法
		request := Request{conn: c, msg: msg}

		// 根据msgID处理对应逻辑
		if utils.GlobalObject.WorkerPoolSize > 0 {
			// 已经开启了工作池,交由工作池处理
			c.MsgHandle.SendMsgToTaskQueue(&request)
		} else {
			go c.MsgHandle.DoMsgHandler(&request)

		}

	}

}

/*
*
写消息Goroutine,处理发送消息
*/
func (c *Connection) StartWriter() {
	// 阻塞的等待channel的消息，进行写数据给客户端
	for {
		select {
		case data := <-c.msgChan:
			//有数据要写给客户端
			if _, err := c.Conn.Write(data); err != nil {
				fmt.Println("Send data error,", err)
				return
			}
		case <-c.ExitChan:
			// 代表Reader已退出，此时Writer也要退出
			return

		}
	}

}

// 启动链接， 让当前的链接准备开始工作
func (c *Connection) Start() {
	fmt.Println("Conn Start()... connID", c.ConnID)
	// 启动从当前链接的读数据的业务
	go c.StartReader()

	//  启动从当前链接写数据的业务
	go c.StartWriter()

	// 按照开发者传递进来的 创建链接后需要调用的处理业务，执行对应的Hook函数
	c.TcpServer.CallOnConnStart(c)
}

// 停止链接， 结束当前链接的工作
func (c *Connection) Stop() {
	fmt.Println("Conn Stop()... ConnID=", c.ConnID)
	// 如果当前链接已经关闭
	if c.isClosed == true {
		return
	}
	c.isClosed = true

	// 按照开发者传递进来的 销毁链接后需要调用的处理业务，执行对应的Hook函数
	c.TcpServer.CallOnConnSop(c)

	// 关闭socket链接
	c.Conn.Close()

	// 告知Writer关闭
	c.ExitChan <- true

	// 将当前链接从ConnManager删除
	c.TcpServer.GetConnManager().Remove(c)

	// 回收资源
	close(c.ExitChan)
	close(c.msgChan)
}

// 获取当前链接绑定的socket conn
func (c *Connection) GetTCPConnection() *net.TCPConn {
	return c.Conn
}

// 获取当前链接模块的链接ID
func (c *Connection) GetConnID() uint32 {
	return c.ConnID
}

// 获取远程客户端的 TCP状态 IP Port
func (c *Connection) RemoteAddr() net.Addr {
	return c.RemoteAddr()

}

// 提供一个SendMsg方法 将我们要发送给客户端的数据，先进行封包
// 发送数据， 将数据发送给远程客户端
func (c *Connection) SendMsg(msgId uint32, data []byte) error {
	if c.isClosed == true {
		return errors.New("Connection closed when send msg")
	}
	// 将data进行封包 MsgDataLen|MsgID|Data
	dp := NewDataPack()
	binaryMsg, err := dp.Pack(NewMsgPackage(msgId, data))
	if err != nil {
		fmt.Println("Pack error msg id=", msgId)
		return errors.New("pack error msg")
	}
	//将数据发送给客户端
	c.msgChan <- binaryMsg

	return nil
}

// 设置链接属性
func (c *Connection) SetProperty(key string, value interface{}) {
	c.propertyLock.Lock()
	defer c.propertyLock.Unlock()

	//设置一个属性
	c.property[key] = value
}

// 获取连接属性
func (c *Connection) GetProperty(key string) (interface{}, error) {
	c.propertyLock.RLock()
	defer c.propertyLock.RUnlock()
	value, ok := c.property[key]
	if ok {
		return value, nil
	} else {
		return nil, errors.New("no property found!")
	}
}

// 移除连接属性
func (c *Connection) RemoveProperty(key string) {
	c.propertyLock.Lock()
	defer c.propertyLock.Unlock()

	//设置一个属性
	delete(c.property, key)
}
